﻿using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

namespace FileMatcher
{
    /// <summary>
    /// Interaction logic for RedunduncySummary.xaml
    /// </summary>
    public partial class RedunduncySummary : INotifyPropertyChanged
    {
        #region Constructors

        public RedunduncySummary(FileMatcherLib.FileMatcher fileMatcher)
        {
            IdenticalFiles = new IdenticalFileList();
            DeleteOps = new List<SelectAndActionOperation>();
            _currOp = 0;
            InitializeComponent();
            FileMatcher = fileMatcher;
            InitializeDialog();
            lvRedundant.ItemsSource = IdenticalFiles;
            DataContext = this;
        }

        #endregion

        #region Events

        #region INotifyPropertyChanged members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #endregion

        #region Properties

        public FileMatcherLib.FileMatcher FileMatcher { get; private set; }

        public IdenticalFileList IdenticalFiles { get; private set; }
        private readonly Dictionary<int, int> _firstIndexOfGroup = new Dictionary<int, int>();
        private List<SelectAndActionOperation> DeleteOps { get; set; }
        private int _currOp;

        public bool Undoable
        {
            get
            {
                return _currOp > 0;
            }
        }

        public bool Redoable
        {
            get
            {
                return _currOp < DeleteOps.Count;
            }
        }

        #endregion

        #region Methods

        void InitializeDialog()
        {
            var igs = FileMatcher.GetIdenticalFiles();
            var groupId = 1;

            // reverse iteration produces groups with size in descending order
            for (var i = igs.Count-1; i >= 0; i--)
            {
                var ig = igs[i];
                if (ig.Count < 1) continue;
                _firstIndexOfGroup[groupId] = IdenticalFiles.Count;
                foreach (var f in ig)
                {
                    var fex = new FileInfoEx(f) {GroupId = groupId};
                    IdenticalFiles.Add(fex);
                }
                groupId++;
            }
            var sortDesc = lvRedundant.Items.SortDescriptions;
            sortDesc.Add(new SortDescription("Length", ListSortDirection.Descending));
            sortDesc.Add(new SortDescription("Name", ListSortDirection.Ascending));
            sortDesc.Add(new SortDescription("DirectoryName", ListSortDirection.Ascending));
        }

        private void LvRedundant_OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            AddGrouping();
        }

        void AddGrouping()
        {
            if (lvRedundant.ItemsSource == null)
            {
                return;
            }

            var myView = (CollectionView)CollectionViewSource.GetDefaultView(lvRedundant.ItemsSource);
            var groupDesc = new PropertyGroupDescription("GroupId");
            if(myView.GroupDescriptions != null)
            {
                myView.GroupDescriptions.Add(groupDesc);
            }
        }

        bool CheckGroup(SelectAndActionOperation delop)
        {
            var allDeletedGroups = new List<int>();
            foreach(var sel in delop.Selected)
            {
                var groupId = sel.GroupId;
                var start = _firstIndexOfGroup[groupId];
                var allDelete = true;
                for(var i = start; i < IdenticalFiles.Count && IdenticalFiles[i].GroupId == groupId; i++)
                {
                    var isToDelete = IdenticalFiles[i].IsSelectedToDelete || delop.Selected.Contains(IdenticalFiles[i]);
                    if (isToDelete) continue;
                    allDelete = false;
                    break;
                }
                if(allDelete)
                {
                    allDeletedGroups.Add(groupId);
                }
            }
            if(allDeletedGroups.Count == 0)
            {
                return true;
            }
            var msgBuilder = new StringBuilder();
            msgBuilder.Append(string.Format("Warning: all files in file group{0} ", allDeletedGroups.Count > 1 ? "s" : ""));
            foreach (var i in allDeletedGroups)
            {
                msgBuilder.Append(string.Format("{0} ", i));
            }
            msgBuilder.Append(string.Format("are going to be deleted. Do you want to proceed?"));
            return (MessageBox.Show(msgBuilder.ToString(), Strings.AppName, 
                MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }

        private void AddSelectAndActionOperation(SelectAndActionOperation delop)
        {
            foreach (var item in lvRedundant.SelectedItems)
            {
                delop.Select((FileInfoEx)item);
            }

            if (delop.Selected.Count == 0)
            {
                return;
            }

            if (delop.ActionType == SelectAndActionOperation.ActionTypes.Delete)
            {
                if (!CheckGroup(delop))
                {
                    return;
                }
            }

            delop.Redo();

            if (_currOp < DeleteOps.Count)
            {
                DeleteOps.RemoveRange(_currOp, DeleteOps.Count - _currOp);
            }
            DeleteOps.Add(delop);
            _currOp++;

            UpdateUndoRedoable();
        }

        private void UpdateUndoRedoable()
        {
            if (PropertyChanged == null)
            {
                return;
            }

            PropertyChanged(this, new PropertyChangedEventArgs("Undoable"));
            PropertyChanged(this, new PropertyChangedEventArgs("Redoable"));
        }

        private void MiUndo_OnClick(object sender, RoutedEventArgs e)
        {
            if (!Undoable) return;
            _currOp--;
            var delop = DeleteOps[_currOp];
            delop.Undo();

            UpdateUndoRedoable();
        }

        private void MiRedo_OnClick(object sender, RoutedEventArgs e)
        {
            if (!Redoable) return;
            var delop = DeleteOps[_currOp];
            delop.Redo();
            _currOp++;

            UpdateUndoRedoable();
        }

        private void MiApply_OnClick(object sender, RoutedEventArgs e)
        {
            var res = MessageBox.Show("Are you sure you want to apply the deleting operations? This is not reversible.",
                Strings.AppName, MessageBoxButton.YesNo);
            if (res != MessageBoxResult.Yes) return;
            var indices = new List<int>();
            for (var i = 0; i < IdenticalFiles.Count; i++)
            {
                var f = IdenticalFiles[i];
                if (!f.IsSelectedToDelete)
                {
                    continue;
                }
                File.Delete(f.FullName);
                indices.Add(i);
            }
            for (var i = indices.Count - 1; i >= 0; i--)
            {
                var index = indices[i];
                IdenticalFiles.RemoveAt(index);
            }

            DeleteOps.Clear();
            _currOp = 0;
            UpdateUndoRedoable();
        }
        
        private void OnCanExecuteOpenFolder(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = lvRedundant.SelectedItems != null && lvRedundant.SelectedItems.Count == 1;
        }

        private void OnExecuteOpenFolder(object sender, ExecutedRoutedEventArgs e)
        {
            var sel = lvRedundant.SelectedItem;
            if (sel == null)
            {
                return;
            }
            var fex = (FileInfoEx)sel;
            System.Diagnostics.Process.Start("explorer.exe", "/select,  \"" + fex.FullName + "\"");
        }

        private void OnCanExecuteDelete(object sender, CanExecuteRoutedEventArgs e)
        {
            if (lvRedundant.SelectedItems == null || lvRedundant.SelectedItems.Count == 0)
            {
                e.CanExecute = false;
                return;
            }
            var canDelete = false;
            foreach (var sel in lvRedundant.SelectedItems)
            {
                var fex = (FileInfoEx)sel;
                if(fex.IsSelectedToDelete) continue;
                canDelete = true;
                break;
            }
            e.CanExecute = canDelete;
        }

        private void OnExecuteDelete(object sender, ExecutedRoutedEventArgs e)
        {
            var delop = new SelectAndActionOperation(IdenticalFiles, SelectAndActionOperation.ActionTypes.Delete);
            AddSelectAndActionOperation(delop);
        }

        private void OnCanExecuteUndelete(object sender, CanExecuteRoutedEventArgs e)
        {
            if (lvRedundant.SelectedItems == null || lvRedundant.SelectedItems.Count == 0)
            {
                e.CanExecute = false;
                return;
            }
            var canUndelete = false;
            foreach (var sel in lvRedundant.SelectedItems)
            {
                var fex = (FileInfoEx)sel;
                if(!fex.IsSelectedToDelete) continue;
                canUndelete = true;
                break;
            }
            e.CanExecute = canUndelete;
        }

        private void OnExecuteUndelete(object sender, ExecutedRoutedEventArgs e)
        {
            var delop = new SelectAndActionOperation(IdenticalFiles, SelectAndActionOperation.ActionTypes.Undelete);
            AddSelectAndActionOperation(delop);
        }

        private void MiClose_OnClick(object sender, RoutedEventArgs e)
        {
            Close();
        }

        #endregion
    }
}
